
;	-----------------------------------------------------------
;
;	"Squeezed" files are generated by the program SQ.COM,
;	and restored by the program USQ.COM (SIG/M xxx), both of
;	which appear to have been compiled from BDS "C". The scheme
;	is to generate a Huffman code, after a preliminary pass
;	which replaces all characters repeated four or more times
;	by a special code - <char><90H><count>. The squeezed file
;	consists of a marker <FF76H>, checksum, name of the original
;	file, decoding dictionary, and finally an encoded copy of
;	the original file.
;
;	The squeezed file terminates with the encoded version of an
;	end marker (non-ASCII 100H) which forms part of the bit
;	stream. The tail end of the original file, on past the ^Z,
;	is encoded with the rest; the tail of the squeezed file is
;	generally trash that is discarded after the [end] marker is
;	encountered. Being both coded and part of the bit stream,
;	its presence is not visible in a Hex dump of the squeezed
;	file.
;
;	TYSQ.ASM will read a squeezed file, typing it on the console.
;	Executing TYSQ.COM with a null command line will produce a
;	tutorial, describing its operation.
;
;		     TYSQ.ASM  Copyright (C) 1984
;		    Universidad Autonoma de Puebla
;			    July 1, 1984
;
;	[Harold V. McIntosh, 1 July 1984]
;	-----------------------------------------------------------

BDOS	equ	224			;BDOS interrupt

tfcb	equ	5CH			;CP/M's FCB
bufr	equ	80H			;input buffer address

;	Non-graphic characters

CR	equ	0DH			;carriage return
LF	equ	0AH			;line feed

;	-------------
	org	0100H
;	-------------

begn:	mov	bx,(offset tfcb+1)	;CP/M's FCB
	cmp	byte ptr [bx],' '
	jnz	notu
	mov	dx,(offset logo)	;'ICUAP' logo
	call	mssg			;send message to console
	jmp	gbye			;return to CP/M
notu:	mov	bx,(offset tfcb+10)	;CP/M's FCB
	cmp	byte ptr [bx],'Q'
	jz	yext
	mov	dx,(offset nsqz)	;'not a squeezed file'
	call	mssg			;send message to console
	jmp	gbye			;return to CP/M

yext:	call	opef			;open input file
	mov	cx,0000			;byte counter, kept in bx
	mov	bctr,cx			;byte counter - input buffer
	mov	smck,cx			;developing checksum
	mov	roco,1			;bit rotation counter

;	Set up code table. Squeezed files seem to begin with the
;	hexadecimal word (FF76) stored in Intel byte order, which
;	would not be likely to start an unsqueezed file.

cota:	call	rbyt			;fetch one byte from input stream
	cmp	al,076H
	jnz	cote			;mssg: 'not squeezed file'
	call	rbyt			;fetch one byte from input stream
	cmp	al,0FFH
	jz	rchk			;read checksum
cote:	mov	dx,(offset nsqz)	;'not a squeezed file'
	call	mssg			;send message to console
	jmp	gbye			;return to CP/M

;	The "squeezed" marker is followed by a two-byte checksum,
;	which is the simple sum of all the one-byte characters in
;	the source file, carried as a two byte sum modulo 2**16.

rchk:	call	rwor			;fetch two bytes from input stream
	mov	cksm,bx			;given checksum

;	Unsqueezed file name. It is an ASCII sequence, may be lower
;	case if SQ.COM received it in response to a prompt, ending
;	with a zero byte. Some trash may be present if SQ.COM wasn't
;	used correctly.

luup:	call	rbyt			;fetch one byte from input stream
	or	al,al
	jz	ldic			;load code dictionary
	call	cona			;type character at console
	jmp	luup

;	Load code dictionary. It is preceded by its two-byte length,
;	and consists of a series of pairs of two-byte addresses. For
;	each bit in the code, select the first element (0) or the
;	second (1) element of the pair. If the pair is positive, it
;	is the table entry (code + 4*index) at which to continue with
;	the next bit. If the pair is negative, it is the complement
;	of the coded ASCII character (low order byte except for [end]).

ldic:	call	rwor			;fetch two bytes from input stream
	mov	cx,bx
	mov	si,(offset code)	;decoding table
looq:	call	rbyt			;fetch one byte from input stream
	mov	[si],al
	inc	si
	call	rbyt			;fetch one byte from input stream
	mov	[si],al
	inc	si
	call	rbyt			;fetch one byte from input stream
	mov	[si],al
	inc	si
	call	rbyt			;fetch one byte from input stream
	mov	[si],al
	inc	si
	loop	looq

	call	crlf			;type CR,LF

;	Start typing unsqueezed code.

tusq:	call	dnch			;decode next character
	jnc G6 ! jmp chek ! G6:		;verify the checksum
	cmp	al,090H			;repeat last character
	jnz	tusu			;normal character
	call	dnch			;decode next character
	dec	al			;get count, adjust it
	mov	ch,al			;set count aside
tust:	mov	al,lach			;last character typed
	call	tpye			;type char & add to checksum
	dec	ch			;update count
	jnz	tust			;repeat until exhausted
	jmp	tusq			;type unsqueezed code

tusu:	mov	lach,al			;last character typed
	call	tpye			;type char & add to checksum
	jmp	tusq			;type unsqueezed code

;	Decode next character.

dnch:	mov	bx,(offset code)	;decoding table
dncr:	call	rbit			;read next bit
	jnc	dncs			;skip for 1, stay for 0
	inc	bx
	inc	bx
dncs:	mov	ax,[bx]			;get next offset
	cmp	ax,0FEFFH		;FEFF means [end]
	jz	dnct
	or	ax,ax
	jns	dncu			;p means new offset
	not	al			;m means complemented char
	stc
	cmc
	ret

dnct:	stc				;flag [end] with carry bit
	ret

;	Calculate <code>+4*<offset>.

dncu:	mov	bx,(offset code)	;decoding table
	add	ax,ax
	add	ax,ax
	add	bx,ax
	jmp	dncr

;	Check for interrupt request.
;	Clear out interrupting character if present.
;	Can't avoid CP/M echoing it when cleared, but at
;	least it won't be waiting to prefix the command
;	line after exit.

break:	mov	cl,11			;(0B) console status
	int	BDOS
	ror	al,1
	jc G12 ! ret ! G12:
	mov	dx,(offset dint)	;'Dump interrupted.'
	call	mssg			;send message to console
	mov	cl,1			;(01) read console
	int	BDOS
	jmp	gbye			;return to CP/M

crlf:	mov	al,CR
	call	cona			;type character at console
	mov	al,LF
	jmp	cona			;type character at console

;	Type unsqueezed text and accumulate checksum.
;	Don't send ^Z's to the console.

tpye:	push	bx
	push	dx
	push	cx
	mov	ch,al
	mov	bx,smck			;developing checksum
	add	al,bl
	mov	bl,al
	mov	al,bh
	adc	al,0
	mov	bh,al
	mov	smck,bx			;developing checksum
	mov	al,ch
	cmp	al,1AH
	jz G13 ! call cona ! G13:	;type character at console
	call	break			;check for interrupt request
	pop	cx
	pop	dx
	pop	bx
	ret

;	Send character in accumulator to console.

cona:	push	bx
	push	dx
	push	cx
	mov	cl,2			;(02) char to console
	mov	dl,al
	int	BDOS
	pop	cx
	pop	dx
	pop	bx
	ret

mssg:	mov	cl,9			;(09) print buffer
	int	BDOS
	ret

;	Verify the checksum.

chek:	mov	bx,cksm			;given checksum
	cmp	bx,smck			;developing checksum
	jnz	chen			;checksum failed
	jmp	gbye			;return to CP/M
chen:	mov	dx,(offset chno)	;'Checksum failure.'
	call	mssg			;send message to console
gbye:	call	crlf			;type CR,LF
	mov	dl,00
	mov	cl,00
	int	BDOS

;	Open input file.

opef:	mov	bx,(offset tfcb+32)
	mov	byte ptr [bx],00
	mov	dx,(offset tfcb)	;CP/M's FCB
	mov	cl,15			;(0F) open file
	int	BDOS
	inc	al
	jz G16 ! ret ! G16:
	mov	dx,(offset nfil)	;'requested file not found'
	call	mssg			;send message to console
	jmp	gbye			;return to CP/M

;	Read one bit at a time.

rbit:	push	bx
	dec	roco			;bit rotation counter
	jnz	rbiu
	mov	roco,8			;bit rotation counter
	call	rbyt
	mov	roby,al			;rotating byte
rbiu:	rcr	roby,1			;rotating byte
	pop	bx
	ret

;	Read one word.

rwor:	call	rbyt			;fetch one byte from input stream
	push	ax
	call	rbyt			;fetch one byte from input stream
	pop	bx
	mov	bh,al
	ret

;	read one byte, refill buffer as needed

rbyt:	cmp	bctr,0000		;byte counter - input buffer
	jnz	rbyu
	push	cx
	push	dx
	call	rdsk
	mov	bptr,(offset bufr)	;byte pointer - input buffer
	mov	bctr,0080H
	pop	dx
	pop	cx
rbyu:	dec	bctr			;byte counter - input buffer
	mov	bx,bptr			;byte pointer - input buffer
	mov	al,(byte ptr[bx])
	inc	bptr			;byte pointer - input buffer
	ret

rdsk:	push	bx
	push	dx
	push	cx
	mov	dx,(offset tfcb)	;CP/M's FCB
	mov	cl,20			;(14) read one record
	int	BDOS
	pop	cx
	pop	dx
	pop	bx
	ret

nfil	db	CR,LF,'Requested file not present.$'
dint	db	CR,LF,'Dump interrupted.$'
nsqz	db	CR,LF,'Not a squeezed file.$'
chno	db	CR,LF,'Checksum failure.$'
logo	db	CR,LF,'Type Squeezed File/ICUAP/July 1, 1984.',CR,LF,CR,LF
	db	'	TYSQ [D:]FILE.?Q?',CR,LF,CR,LF
	db	'will unsqueeze FILE.?Q? and type it at the',CR,LF
	db	'console. Press any key to stop the display.',CR,LF
	db	'$'

	rb	16
stak	rw	1
lach	rb	1		;last character typed
roco	rb	1		;rotating bit counter
roby	rb	1		;rotating byte
bctr	rw	1		;byte counter - input buffer
bptr	rw	1		;byte pointer - input buffer
cksm	rw	1		;given checksum
smck	rw	1		;developing checksum
code	rb	1024		;decoding table

	end
